package clean_handler

import (
	"file-cleaner/internal/file_cleaner/cleaner"
	"file-cleaner/internal/file_cleaner/file_info"
	v1log "github.com/ClessLi/component-base/pkg/log/v1"
	"os"
)

type BackupForLinkHandlerImpl struct {
	collector *CleanTaskInfoCollector
}

func NewBackupForLinkHandler(collector *CleanTaskInfoCollector) CleanHandler {
	return &BackupForLinkHandlerImpl{collector: collector}
}

func (b BackupForLinkHandlerImpl) doHandle(info *cleaner.CleanTaskInfo) bool {
	if info.SourceFileInfo().GetStat().Nlink > 1 {
		v1log.Debug("source file has hard link file")
		err := info.TargetFileInfo().RefreshStat()
		if err != nil {
			v1log.Errorf("Refresh target file(%v) stat failed, cased by: %v", info.TargetFileInfo().Path(), err)
			return false
		}
		if info.SourceFileInfo().GetStat().Nlink > info.TargetFileInfo().GetStat().Nlink {
			// backup target
			err = os.Rename(info.TargetFileInfo().Path(), info.TargetFileInfo().Path()+"."+backupSuffix)
			if err != nil {
				v1log.Error(err.Error())
				return false
			}
			v1log.Debugf("Hard linking targe file(%v) to source file(%v)...", info.TargetFileInfo().Path(), info.SourceFileInfo().Path())
			err = os.Link(info.SourceFileInfo().Path(), info.TargetFileInfo().Path())
			if err != nil {
				v1log.Error(err.Error())
				// rollback target
				err = os.Rename(info.TargetFileInfo().Path()+"."+backupSuffix, info.TargetFileInfo().Path())
				if err != nil {
					v1log.Error(err.Error())
				}
				return false
			}
			err = info.SourceFileInfo().SetTag(file_info.SkipOperation)
			if err != nil {
				v1log.Errorf("Skip cleaning tasks failed, casd by: %v", err)
				// rollback target
				err = os.Rename(info.TargetFileInfo().Path()+"."+backupSuffix, info.TargetFileInfo().Path())
				if err != nil {
					v1log.Error(err.Error())
				}
				return false
			}
			b.collector.Record(info, false)
			v1log.Infof("Skip cleaning tasks, because the target file is hard linked to the source file(%v)", info.SourceFileInfo().Path())
			// remove target's backup
			err = os.Remove(info.TargetFileInfo().Path() + "." + backupSuffix)
			if err != nil {
				v1log.Errorf("Remove target file's backup(%v) failed, cased by: %v", info.TargetFileInfo().Path()+"."+backupSuffix, err)
			}
			return true
		}
	}

	err := os.Link(info.SourceFileInfo().Path(), info.SourceFileInfo().Path()+"."+backupSuffix)
	if err != nil {
		v1log.Error(err.Error())
		return false
	}
	v1log.Infof("File: %v backed up as %v", info.SourceFileInfo().Path(), info.SourceFileInfo().Path()+"."+backupSuffix)
	return true
}

type SkipSameFileHandlerImpl struct {
	collector *CleanTaskInfoCollector
}

func NewSkipSameFileHandler(collector *CleanTaskInfoCollector) CleanHandler {
	return &SkipSameFileHandlerImpl{collector: collector}
}

func (s SkipSameFileHandlerImpl) doHandle(info *cleaner.CleanTaskInfo) bool {
	if info.SourceFileInfo().GetTag() != file_info.OnlyDelete && info.IsSamePath() {
		err := info.SourceFileInfo().SetTag(file_info.SkipOperation)
		if err != nil {
			v1log.Errorf("Skip cleaning tasks for Non Deletion failed, casd by: %v", err)
			return false
		}
		s.collector.Record(info, false)
		v1log.Infof("Skip cleaning tasks for Non Deletion, because the target file is the same file as the source file(%v)", info.SourceFileInfo().Path())
	}
	v1log.Debugf("Do not skip cleaning tasks for source file: %v", info.SourceFileInfo().Path())
	return true
}

type SkipSameInodeFilesHandlerImpl struct {
	collector *CleanTaskInfoCollector
}

func NewSkipSameInodeFilesHandler(collector *CleanTaskInfoCollector) CleanHandler {
	return &SkipSameInodeFilesHandlerImpl{collector: collector}
}

func (s SkipSameInodeFilesHandlerImpl) doHandle(info *cleaner.CleanTaskInfo) bool {
	if info.IsSameLinkFile() {
		err := info.SourceFileInfo().SetTag(file_info.SkipOperation)
		if err != nil {
			v1log.Errorf("Skip cleaning tasks for Same Inode Files failed, casd by: %v", err)
			return false
		}
		s.collector.Record(info, false)
		v1log.Infof("Skip cleaning tasks for Same Inode Files, because the target file's inode is the same as the source file(%v)'s", info.SourceFileInfo().Path())
	}
	v1log.Debugf("Do not skip cleaning tasks for source file: %v", info.SourceFileInfo().Path())
	return true
}
